from collections import Counter
from collections import deque
import datetime
import math
import numpy as np
import cv2


class direction():
    def __init__(self):
        self.memory = {}
        self.up_count = 0
        self.down_count = 0
        # self.line = [(0, int(0.5 * img.shape[0])), (int(img.shape[1]), int(0.5 * img.shape[0]))]
        self.already_counted = deque(maxlen=50)  # 存放已经计数的追踪目标
        self.total_counter = 0  # 出入通过的总人数

    # 线与线的碰撞检测：叉乘的方法判断两条线是否相交
    # 计算叉乘符号
    def ccw(self, A, B, C):

        return (C[1] - A[1]) * (B[0] - A[0]) > (B[1] - A[1]) * (C[0] - A[0])

    # 检测AB和CD两条直线是否相交
    def is_intersection(self, A, B, C, D):
        # print('is_intersection', self.ccw(A, C, D), self.ccw(B, C, D), self.ccw(A, B, C), self.ccw(A, B, D))
        return self.ccw(A, C, D) != self.ccw(B, C, D) and self.ccw(A, B, C) != self.ccw(A, B, D)

    # 将坐标格式化，转换为中心点坐标
    def tlbr_midpoint(self, box):
        '''
        Finds midpoint of a box in tlbr format.
        (top left, bottom right)
        '''
        minX, minY, maxX, maxY = box
        t = 1 / 2  # 中心坐标系数，可以控制中心点的纵坐标
        midpoint = (int((minX + maxX) / 2), int((minY + maxY) * t))  # minus y coordinates to get proper xy format
        return midpoint

    # 计算追踪目标中心点与划线所围成的区域面积
    def direct(self, midpoint, line):

        direct_x = ((midpoint[0] - line[0][0]) * (line[1][1] - line[0][1]))
        direct_y = ((midpoint[1] - line[0][1]) * (line[1][0] - line[0][0]))
        direct = direct_x - direct_y
        return direct

    # 处理边界框， 转换为 划线
    def process_frame(self, point_list, width, height, lr=0.05):
        line_list = []

        # 若区域框中的端点靠近边界，则将其忽略
        for point in point_list:
            if (((int(width) - int(point[1])) < lr * int(width)) \
                    or ((int(width) - int(point[1])) > (1-lr) * int(width))) \
                and (((int(height) - int(point[0])) < lr * int(height)) \
                    or ((int(height) - int(point[0])) > (1-lr) * int(height))):
                continue
            else:
                line_list.append(point)
        print('line_list', line_list)

        return line_list

    def state_frame(self, line_list):
        #   判断检测框位置，选择用划线逻辑或者用画框逻辑
        if len(line_list) > 2:
            state_frame = True   # 用于区分划线和画框逻辑状态位
        else:
            state_frame = False
        return state_frame

    def line_direction(self, img, line_list, point_list, bbox, identities=None):

        #  划线的信息
        # line = [(0, int(0.5 * img.shape[0])), (int(img.shape[1]), int(0.5 * img.shape[0]))]
        # line = [(int(0.5 * img.shape[1]), 0), (int(0.5 * img.shape[1]), int(img.shape[0]))]
        flag = False  # 用于判断人过线时的标记
        stay_count = 0  # 用于进行停留人数的统计

        for i, box in enumerate(bbox):
            # x1, y1, x2, y2 = [int(i) for i in box]
            line = line_list
            # 	当前跟踪目标的中心坐标
            midpoint = self.tlbr_midpoint(box)
            id = int(identities[i]) if identities is not None else 0

            # 先判断追踪的id号是否已经在 memory字典当中，若不在，创建一个队列
            if id not in self.memory:
                self.memory[id] = deque(maxlen=2)
            # 在字典中添加当前追踪节点的中心点坐标，添加是放在队尾,若超出长度，则会让队尾的出队
            self.memory[id].append(midpoint)
            #  取出对应id上一次的中心点坐标信息
            previous_midpoint = self.memory[id][0]
            # 作图，画出前后中心点的轨迹图
            # print('midpoint, previous_midpoint:', midpoint, previous_midpoint, line[0], line[1])
            cv2.line(img, midpoint, previous_midpoint, (0, 255, 0), 2)

            # 计算追踪目标出入方向
            # 检测目标的中心点
            erea_np = np.array(point_list, np.int32)
            num_center = cv2.pointPolygonTest(erea_np, midpoint, False)
            if num_center >= 0:
                stay_count += 1

            # 第1步，通过点的数量判断采用划线或画框逻辑
            if self.state_frame(line_list):
                pre_num_center = cv2.pointPolygonTest(erea_np, previous_midpoint, False)
                # 第2步，判断上下针中心位置状态是否变化
                # and id not in self.already_counted   测试时不启用，在实际上线时需添加的判断条件
                # if (num_center == 0 and pre_num_center != 0) or (num_center != 0 and pre_num_center == 0):
                # 注： np.sign 中已经有对0 的处理，为零时符号为 0.

                # 第3步，判断当前帧中心位置是否在框中
                if np.sign(num_center) != np.sign(pre_num_center):
                    self.total_counter += 1
                    # Set already counted for ID to true.
                    self.already_counted.append(id)
                    if num_center >= 0:
                        self.up_count += 1
                    else:
                        self.down_count += 1

                line.append(line_list[0])
                for i in range(len(line) - 1):
                    if self.is_intersection(midpoint, previous_midpoint, line[i], line[i+1]):
                        cv2.line(img, line[i], line[i+1], (0, 0, 255), 2)
                        break
            else:
                #  第2步，若两条线段相交，则开始统计进出流量
                # and id not in self.already_counted   测试时不启用，在实际上线时需添加的判断条件
                if self.is_intersection(midpoint, previous_midpoint, line[0], line[1]):
                    self.total_counter += 1

                    flag = True

                    # Set already counted for ID to true.
                    self.already_counted.append(id)

                    # 这是从面积正负性的方式判断方向
                    # direction = self.direct(midpoint, line)

                    # 第3步，这里从中心点坐标是否在区域框中判断方向
                    if num_center >= 0:
                        self.up_count += 1
                    else:
                        self.down_count += 1

            # Delete memory of old tracks.
            # This needs to be larger than the number of tracked objects in the frame.
            if len(self.memory) > 50:
                del self.memory[list(self.memory)[0]]

        return img, self.total_counter, self.up_count, self.down_count, stay_count, flag

    # 作图，画出图中的区域框和检测流量的数量
    def draw_line(self, img, line_list, total_counter, up_count, down_count, stay_count, flag):

        # line = [(0, int(0.5 * img.shape[0])), (int(img.shape[1]), int(0.5 * img.shape[0]))]
        # line = [(int(0.5 * img.shape[1]), 0), (int(0.5 * img.shape[1]), int(img.shape[0]))]
        line = line_list

        # draw yellow line  在图中画出检测线  flag 区分是否跨线标志， 跨线画红线
        if flag:
            cv2.line(img, line[0], line[1], (0, 0, 255), 2)

        # Draw total count.
        cv2.putText(img, "Total: {} ({} up, {} down, {} stay)"
                    .format(str(total_counter), str(up_count), str(down_count), str(stay_count)),
                    (int(0.05 * img.shape[1]), int(0.1 * img.shape[0])), 0,
                    1.5e-3 * img.shape[0], (0, 255, 255), 2)

        return img

    def draw_detection(self, image, point_list):
        """
        在原图上画出框选区域
        :param image: 图片地址
        :param point_list: 框选区域所有点的坐标 list 格式
        """

        pts = np.array(point_list, np.int32)
        pts = pts.reshape((-1, 1, 2))
        cv2.polylines(image, [pts], True, (0, 255, 255), 2)
        return image





